package com.example.common.utils;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.concurrent.TimeUnit;

/**
 * 断点续传工具类
 */
public class BreakpointResume {
	/**
	 * 断点续传时每段的字节数，建议不要设置得太大
	 */
	private static final long FRAGMENT_SIZE = 2048L;
	/**
	 * 文件存储目录
	 */
	private static final String LOCAL_PATH = "http://47.100.198.147:7791/im-system";

	/**
	 * 进行断点续传下载
	 *
	 * @param url       目标资源的URL地址
	 * @param localFile 本地文件的绝对路径 + 文件名（可选）
	 * @throws Exception 如果发生错误
	 */
	public static void downloadWithResume(String url, String localFile) throws Exception {
		// 校验URL格式
		if (!isValidURL(url)) {
			System.err.println("Invalid URL format.");
			return;
		}
		// 校验本地文件格式（如果提供）
		if (localFile != null && !isValidLocalFile(localFile)) {
			System.err.println("Invalid local file format.");
			return;
		}

		// 计算起始下载位置
		long startRange = localFile == null ? 0L : new File(localFile).length();
		long endRange = startRange + FRAGMENT_SIZE;

		// 进行分段下载
		downloadPartially(url, localFile, startRange, endRange);
	}

	/**
	 * 分段下载方法
	 *
	 * @param url       目标资源的URL地址
	 * @param localFile 本地文件路径（可选）
	 * @param startRange 下载起始位置
	 * @param endRange   下载结束位置
	 * @throws MalformedURLException 如果URL格式不正确
	 */
	private static void downloadPartially(String url, String localFile, long startRange, long endRange) throws MalformedURLException {
		long startTime = System.currentTimeMillis();
		URL resource = new URL(url);

		while (true) {
			HttpURLConnection conn = null;
			InputStream in = null;
			RandomAccessFile raf = null;
			try {
				// 创建HTTP连接
				conn = (HttpURLConnection) resource.openConnection();
				// 设置请求属性Range，实现断点续传
				conn.setRequestProperty("Range", "bytes=" + startRange + "-" + (endRange == -1L ? "" : endRange));
				conn.connect();

				// 获取响应状态码
				String statusLine = conn.getHeaderField(null);
				if (!statusLine.contains("200") && !statusLine.contains("206")) {
					throw new Exception("Invalid response status: " + statusLine);
				}

				// 获取Content-Range信息
				String cr = conn.getHeaderField("Content-Range");
				if (cr == null || cr.trim().isEmpty()) {
					throw new Exception("Missing Content-Range header");
				}
				cr = cr.replace("bytes", "").trim();
				long resourceEndPos = Long.parseLong(cr.split("-")[1].split("/")[0]);
				long resourceTotalLength = Long.parseLong(cr.split("/")[1]);

				// 读取下载内容
				byte[] buf = new byte[(int) (resourceEndPos - startRange)];
				in = conn.getInputStream();
				in.read(buf);

				// 如果本地文件未指定，则创建临时文件
				if (localFile == null) {
					localFile = LOCAL_PATH + File.separator + new File(url).getName() + ".tmp";
				}
				File f = new File(localFile);
				if (!f.exists()) {
					new FileOutputStream(f).close();
				}

				// 休眠10ms，确保文件句柄释放
				TimeUnit.MILLISECONDS.sleep(10L);
				raf = new RandomAccessFile(f, "rwd");
				raf.seek(startRange);
				raf.write(buf);

				// 检查是否已下载完整
				if (resourceEndPos == resourceTotalLength - 1) {
					f.renameTo(new File(LOCAL_PATH + File.separator + new File(url).getName()));
					break;
				}

				// 计算下一次下载范围
				startRange = resourceEndPos;
				endRange = resourceTotalLength - resourceEndPos <= FRAGMENT_SIZE * 3 / 2 ? -1L : resourceEndPos + FRAGMENT_SIZE;
			} catch (Exception e) {
				System.err.println(e.getMessage());
				return;
			} finally {
				// 关闭流和断开连接
				try { if (in != null) in.close(); } catch (Exception ignored) {}
				if (conn != null) conn.disconnect();
			}
		}
		System.out.println("Download completed in " + (System.currentTimeMillis() - startTime) + " ms");
	}

	/**
	 * 校验URL格式
	 *
	 * @param url 待校验的URL字符串
	 * @return 是否为合法的URL
	 */
	private static boolean isValidURL(String url) {
		try {
			new URL(url).toURI();
			return url.startsWith("http://") || url.startsWith("https://");
		} catch (Exception e) {
			return false;
		}
	}

	/**
	 * 校验本地文件格式
	 *
	 * @param localFile 本地文件路径
	 * @return 是否为合法的本地文件
	 */
	private static boolean isValidLocalFile(String localFile) {
		File file = new File(localFile);
		return file.exists() && file.isFile() && localFile.endsWith(".tmp");
	}
}
